#!/usr/bin/env node
const xlsx = require('node-xlsx');
const fs = require('fs');
const path = require('path');
const LZString = require('lz-string');
const { program } = require('commander');
program
    .option('-c, --compress', "是否压缩")
    .option('-s, --source <string>', "excel 文件路径")// excel 文件路径
    .option('-o, --output <string>', "json 文件路径")// json 文件路径
    .option('-to, --typescriptOutput <string>', "typescript class文件路径")// typescript 文件路径
    .option('-os, --outputServer <string>', "服务端json文件路径,如为空则不会生产服务的配置文件");// 服务端json文件路径


program.parse();
const options = program.opts();
// console.log(program.args, options);
let excelFolderPath = './excel';
let jsonFolderPath = './json';
let classFolderPath = './class';
let serverJsonFolderPath = ""

if (options.source) {
    excelFolderPath = process.cwd() + options.source
}
if (options.output) {
    jsonFolderPath = process.cwd() + options.output
}
if (options.outputServer) {
    serverJsonFolderPath = process.cwd() + options.outputServer
}

if (options.typescriptOutput) {
    classFolderPath = process.cwd() + options.typescriptOutput
} else if (options.output) {
    classFolderPath = process.cwd() + options.output
}
const isCompress = options.compress

function deleteFilesInFolder(folderPath, ext) {
    // 读取文件夹中的所有文件和子文件夹
    const files = fs.readdirSync(folderPath);

    // 遍历文件和子文件夹
    files.forEach((file) => {
        if (ext) {
            const extName = path.extname(file);
            if (extName !== ext) {
                return
            }
        }

        const filePath = path.join(folderPath, file);

        // 检查文件的类型
        const stats = fs.statSync(filePath);
        if (stats.isFile()) {
            // 如果是文件，则删除
            fs.unlinkSync(filePath);
            //console.log(`已删除文件: ${filePath}`);
        } else if (stats.isDirectory()) {
            // 如果是文件夹，则递归调用删除函数
            deleteFilesInFolder(filePath, ext);
        }
    });
}

function parseValue(value, type) {
    type = type.toLowerCase();
    if (type.startsWith("array") || type.endsWith("]")) {
        type = "array"
    }
    switch (type) {
        case 'string':
            return String(value || "");
        case 'number':
            return Number(value) || 0;
        case 'boolean':
        case 'bool':
            return !!(value && type.toLowerCase() != 'false' && value != '0');
        case 'array':
            //return JSON.parse(value);
            if (!value) {
                return [];
            }
            if (!(value + "").startsWith("[")) {
                value = `[${value}]`
            }
            return eval(`(${value})`);
        case 'object':
            //return JSON.parse(value);
            return eval(`(${value})`);
        default:
            return value;
    }
}

function getTypeScriptType(obj, type) {

    type = type || typeof obj;


    if (type.endsWith("]")) {
        return type
    }

    if (type === 'object') {

        if (Array.isArray(obj)) {
            let arrayType = getTypeScriptType(obj[0]);
            if (arrayType == "undefined") {
                arrayType = 'any';
            }

            return `${arrayType}[]`;
        } else if (obj === null) {
            return 'null';
        } else {
            const properties = Object.entries(obj).map(([key, value]) => {
                const propertyType = getTypeScriptType(value);
                return `${key}: ${propertyType}`;
            });
            return `{ ${properties.join(', ')} }`;
        }
    } else if (type === 'array' || type === '[]') {
        if (Array.isArray(obj)) {
            let arrayType = getTypeScriptType(obj[0]);
            if (arrayType == "undefined") {
                arrayType = 'any';
            }
            return `${arrayType}[]`;
        } else {
            return "any[]";
        }
    } else if (type === 'array<number>') {
        return "number[]";
    } else if (type === 'array<string>') {
        return "string[]";
    } else if (type === 'array<bool>' || type === 'array<boolean>') {
        return "boolean[]";
    } else if (type === 'array<array<number>>') {
        return "number[][]";
    } else if (type === 'array<array<string>>') {
        return "string[][]";
    } else if (type === 'array<array<bool>>' || type === 'array<array<boolean>>') {
        return "boolean[][]";
    }
    else {
        return typeof obj;
    }
}


/**确认class文件夹是否存在 */
if (!fs.existsSync(classFolderPath)) {
    fs.mkdirSync(classFolderPath, { recursive: true });
}
/**删除class文件夹下所有文件 */
deleteFilesInFolder(classFolderPath, ".ts")


/**确认json文件夹是否存在 */
if (!fs.existsSync(jsonFolderPath)) {
    fs.mkdirSync(jsonFolderPath, { recursive: true });
}
/**删除json文件夹下所有文件 */
deleteFilesInFolder(jsonFolderPath, isCompress ? ".txt" : ".json")


if (serverJsonFolderPath) {
    /**确认json文件夹是否存在 */
    if (!fs.existsSync(serverJsonFolderPath)) {
        fs.mkdirSync(serverJsonFolderPath, { recursive: true });
    }
    /**删除json文件夹下所有文件 */
    deleteFilesInFolder(serverJsonFolderPath, "json")
}

/**解析sheet页名称 */
function parseExportName(name) {
    const nameArr = name.split('&');
    if (nameArr.length > 1) {
        return nameArr[1];
    } else {
        return nameArr[0];
    }
}

/**解析sheet页分组文件名称 */
function parseSheetGroupFileName(name) {
    const nameArr = name.split('&');
    if (nameArr.length == 3) {
        return nameArr[2];
    } else {
        return "";
    }
}

/**解析列表型数据表 */
function parseTableSheet(sheet, isClient) {
    /**字段名 */
    const programTypes = sheet.data[0];
    /**字段名 */
    const keys = sheet.data[1];
    /**描述 */
    const descs = sheet.data[2];
    /**数据类型 */
    const types = sheet.data[3];

    const jsonData = [];

    //console.log(`开始导出: ${sheetName}`);
    // 遍历每行数据
    for (let row = 4; row < sheet.data.length; row++) {
        const record = {};
        for (let col = 0; col < keys.length; col++) {
            const programType = programTypes[col];

            if (isClient && programType == "server") {
                continue
            } else if (!isClient && programType == "client") {
                continue
            }
            const key = keys[col];
            const type = types[col];
            const rowData = sheet.data[row];
            const value = rowData[col];

            if (!keys[col] || !rowData[0]) {
                //过滤掉第一行或第一列是空的
                continue;
            }
            if (!type) {
                throw (` ${sheet.name}  第${col + 1}列，类型未填写`, types)
            }
            try {
                record[key] = parseValue(value, type);
            } catch (error) {
                console.log(`error =====》 sheet:${sheet.name}  第${row + 1}行 第${col + 1}列 value:${value} type:${type}`)
                throw (error)
            }

        }
        if (Object.keys(record).length > 0) {
            jsonData.push(record);
        }


    }
    return jsonData
}

clientGroupMap = {}

/**处理列表型表格sheet页 */
function handleTableSheet(sheet, filePath, fileName) {

    /**导出使用sheet名还是文件名 */
    let exportUseName = sheet.name == `Sheet1` ? fileName : sheet.name
    /**分组文件名 */
    let groupFileName = parseSheetGroupFileName(exportUseName);

    const exportName = parseExportName(exportUseName);


    /**字段名 */
    const keys = sheet.data[1];
    /**描述 */
    const descs = sheet.data[2];
    /**数据类型 */
    const types = sheet.data[3];



    const clientJsonData = parseTableSheet(sheet, true);
    if (clientJsonData.length > 0) {
        const jsonFileName = `${exportName}.${isCompress ? "txt" : "json"}`;
        const jsonFilePath = path.join(jsonFolderPath, jsonFileName);


        /**配置了分组文件 */
        if (groupFileName) {
            clientGroupMap[groupFileName] = clientGroupMap[groupFileName] || []
            clientGroupMap[groupFileName].push({
                sheet,
                filePath,
                sheetName: exportUseName,
                data: clientJsonData,
                isTable: true,
                fileName:exportUseName
            })
        } else {
            const fileData = isCompress ? LZString.compressToUTF16(JSON.stringify(clientJsonData, null, 2)) : JSON.stringify(clientJsonData, null, 2);
            /** 将JSON数据写入文件*/
            fs.writeFileSync(jsonFilePath, fileData);
            console.log(`client JSON已导出: ${path.basename(filePath)}/${exportName} -> ${jsonFolderPath}/${jsonFileName}`);
            exportTsClassFile(sheet, exportUseName, clientJsonData, true)
        }



    }


    if (options.outputServer) {
        const serverJsonData = parseTableSheet(sheet);
        if (serverJsonData.length > 0) {
            // 将JSON数据写入文件
            const jsonFileName = `${exportName}.json`;
            const jsonFilePath = path.join(serverJsonFolderPath, jsonFileName);
            const fileData = JSON.stringify(serverJsonData, null, 2);
            fs.writeFileSync(jsonFilePath, fileData);
            console.log(`server JSON已导出: ${path.basename(filePath)}/${exportName} -> ${serverJsonFolderPath}/${jsonFileName}`);
        }
    }
}


function getTsClassDef(sheet, fileName, clientJsonData, isTable) {
    /**字段名 */
    const keys = sheet.data[1];
    /**描述 */
    const descs = sheet.data[2];
    /**数据类型 */
    const types = sheet.data[3];

    const exportName = `${parseExportName(fileName)}`;
    

    let classDefText = ""
    if (isTable) {
        const classFileName = `${exportName}.ts`;
        // const classFilePath = path.join(classFolderPath, classFileName);
        classDefText = `/**${fileName.split('&')[0]} */\n@regConfig("${exportName}")\nexport class ${exportName} {\n${Object.keys(clientJsonData[0]).map(
            (key) => {
                const keyIndex = keys.indexOf(key);
                const type = types[keyIndex]

                const comment = descs[keyIndex] ? `    /**${descs[keyIndex]} */\n` : '';
                return `${comment}    ${key}: ${getTypeScriptType(clientJsonData[0][key], type)};`

            }

        ).join('\n')}\n}\n`;
    } else {
        /**生成class文件 */
        const classFileName = `${exportName}.ts`;
        // const classFilePath = path.join(classFolderPath, classFileName);
        let index = 0
        classDefText = `/**${fileName.split('&')[0]} */\n@regConfig("${exportName}")\nexport class ${exportName} {\n${Object.keys(clientJsonData).map(
            (key) => {
                const rowData = sheet.data[index + 2];
                const type = rowData[1];
                const value = clientJsonData[key];
                const desc = rowData[3];
                const comment = desc ? `    /**${desc} */\n` : '';
                index++
                return `${comment}    ${key}: ${getTypeScriptType(value, type)};`

            }
        ).join('\n')}\n}\n`;
    }
    return classDefText
}

/** */
function exportTsClassFile(sheet, fileName, clientJsonData, isTable) {
    const classFileName = `${parseExportName(fileName)}.ts`;
    const classFilePath = path.join(classFolderPath, classFileName);
    const text = getTsClassDef(sheet, fileName, clientJsonData, isTable)
    allTsDefStr += text + "\n"
    fs.writeFileSync(classFilePath, text);
}

function exportGroupTsClassFile(
    fileName,
    clientGroup
) {
    
    const text = clientGroup.map(item => getTsClassDef(item.sheet, item.fileName, item.data, item.isTable)).join("\n")
    const classFileName = `${parseExportName(fileName)}.ts`;
    const classFilePath = path.join(classFolderPath, classFileName);
    allTsDefStr += text
    fs.writeFileSync(classFilePath, text);

}



//**解析对象型数据表 */
function parseObjectSheet(sheet, isClient) {
    const sheetName = parseExportName(sheet.name);
    const jsonData = {};

    // 遍历每行数据
    for (let row = 2; row < sheet.data.length; row++) {

        const rowData = sheet.data[row];

        const use = rowData[4];
        if (isClient && use == "server") {
            continue
        } else if (!isClient && use == "client") {
            continue
        }

        const key = rowData[0];
        const type = rowData[1];
        const value = rowData[2];
        const desc = rowData[3];

        if (!key) {
            //过滤掉第一列是空的
            continue;
        }
        if (!type) {
            throw (`type is null in ${sheetName} row:${row}`)
        }
        jsonData[key] = parseValue(value, type);

    }
    return jsonData;
}
/**处理kv型表格sheet页 */
function handleObjectSheet(sheet, filePath, fileName) {
    /**导出使用sheet名还是文件名 */
    let exportUseName = sheet.name == `Sheet1` ? fileName : sheet.name
    /**分组文件名 */
    let groupFileName = parseSheetGroupFileName(exportUseName);



    const sheetName = parseExportName(exportUseName);
    const clientJsonData = parseObjectSheet(sheet, true);
    /**前端json处理 */
    if (Object.keys(clientJsonData).length > 0) {
        // 将JSON数据写入文件
        const jsonFileName = `${sheetName}.${isCompress ? "txt" : "json"}`;
        const jsonFilePath = path.join(jsonFolderPath, jsonFileName);

        //**配置了分组文件 */
        if (groupFileName) {
            clientGroupMap[groupFileName] = clientGroupMap[groupFileName] || []
            clientGroupMap[groupFileName].push({
                sheet,
                filePath,
                sheetName:exportUseName,
                data: clientJsonData,
                isTable: false,
                fileName:exportUseName
            })
        } else {
            const fileData = isCompress ? LZString.compressToUTF16(JSON.stringify(clientJsonData, null, 2)) : JSON.stringify(clientJsonData, null, 2);
            fs.writeFileSync(jsonFilePath, fileData);
            console.log(`client JSON已导出: ${path.basename(filePath)}/${exportUseName} -> ${jsonFolderPath}/${jsonFileName}`);
            exportTsClassFile(sheet, exportUseName, clientJsonData, false)

        }

    }


    //**后端json导出 */
    if (options.outputServer) {
        const serverJsonData = parseObjectSheet(sheet, false);
        if (Object.keys(serverJsonData).length > 0) {
            // 将JSON数据写入文件
            const jsonFileName = `${sheetName}.json`;
            const jsonFilePath = path.join(serverJsonFolderPath, jsonFileName);
            const fileData = JSON.stringify(serverJsonData, null, 2);

            fs.writeFileSync(jsonFilePath, fileData);

            console.log(`server JSON已导出: ${path.basename(filePath)}/${sheetName} -> ${serverJsonFolderPath}/${jsonFileName}`);
        }
    }



}

function getAllFiles(fromPath, outPaths, exts = [`.xlsx`, `.xls`]) {
    try {
        var stat = (0, fs.statSync)(fromPath);
        if (stat.isDirectory()) {
            var files = (0, fs.readdirSync)(fromPath);
            files.forEach(function (f) {
                var ifurl = (0, path.join)(fromPath, f);
                getAllFiles(ifurl, outPaths);
            });
        }
        else if (stat.isFile()) {
            var ext = (0, path.extname)(fromPath);
            if (exts.includes(ext)) {
                outPaths.push(fromPath);
            }
        }
    }
    catch (e) {
        console.log(e);
    }
}

let allTsDefStr = ""

const excelPaths = []
getAllFiles(excelFolderPath, excelPaths)
console.log("开始解析Excel文件", excelPaths);
excelPaths.forEach(filePath => {
    const fileName = path.basename(filePath)
    const fileBaseName = fileName.split(".")[0]
    const ext = path.extname(fileName)
    console.log(ext, fileBaseName, filePath);

    if (fileBaseName.startsWith("#")) {
        return
    }
    // 判断文件是否为Excel文件
    if (fileName.indexOf('~$') == -1) {
        // 读取Excel文件
        const sheets = xlsx.parse(filePath);

        // 遍历每个Sheet表
        sheets.forEach(sheet => {
            if (sheet.name.startsWith("#") || sheet.name.indexOf("|") == -1 && sheet.name != "Sheet1") {
                return
            }
            const row1 = sheet.data[0];
            try {
                if (row1[0] == 'key' && row1[1] == 'type' && row1[2] == 'value' && row1[3] == 'desc') {
                    handleObjectSheet(sheet, filePath, fileBaseName);
                } else {
                    handleTableSheet(sheet, filePath, fileBaseName)

                }
            } catch (error) {
                console.log(`error =====》file:${fileName}, sheetName:${sheet.name}`)
                throw (error)
            }
        });
    }

})

const groupFileNames = Object.keys(clientGroupMap)

groupFileNames.forEach(fileName => {
    
    // 将JSON数据写入文件
    const clientGroup = clientGroupMap[fileName]

    let groupFileName = parseSheetGroupFileName(clientGroup[0].fileName);
    const jsonFileName = `${groupFileName}.${isCompress ? "txt" : "json"}`;
    const jsonFilePath = path.join(jsonFolderPath, jsonFileName);
    const sheetData = {}
    clientGroup.forEach(sheet => {
        sheetData[parseExportName(sheet.fileName)] = sheet.data
    })
    // console.log(sheetData)
    const fileData = isCompress ? LZString.compressToUTF16(JSON.stringify(sheetData, null, 2)) : JSON.stringify(sheetData, null, 2);

    fs.writeFileSync(jsonFilePath, fileData);
    console.log(`client JSON已导出: ${jsonFolderPath}/${jsonFileName}`);
    exportGroupTsClassFile(fileName, clientGroup)
})

const allClassFilePath = path.join(classFolderPath, "all.ts");
fs.writeFileSync(allClassFilePath, allTsDefStr);
